﻿using MvaLib.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.ConversationalAgent;
using Windows.Foundation;
using Windows.System;
using Windows.System.Display;

namespace Mva.Recognition
{

    public class MsSpeechRecognizerConfig
    {
        public string SubscriptionKey { get; set; }
        public string AzureRegion { get; set; }
        public string CustomSpeechId { get; set; }
    }

    public static class Recognizer
    {
        public static bool EnableSecondStageKws
        {
            get
            {
                return LocalSettingsHelper.EnableSecondStageKws;
            }
            set
            {
                LocalSettingsHelper.EnableSecondStageKws = value;
            }
        }
        public static bool SaveKeywordEnabled {
            get
            {
                return DialogManager.SaveKeywordEnabled;
            }
            set
            {
                DialogManager.SaveKeywordEnabled = value;
            }
        } 
        public static bool IsSessionExceptionAccured { get; private set; } = false;
        public static event Action<string> SessionExceptionAccured;
        public static event Action<string> SetupConversationFailed;

        public delegate void KeywordEventHandler();

        public static event KeywordEventHandler KeywordSetupFinished;
        public static event KeywordEventHandler KeywordConfirmed;
        public static event KeywordEventHandler KeywordRejected;
        public static event TypedEventHandler<ActivationSignalDetectionConfiguration, DetectionConfigurationAvailabilityChangedEventArgs> AvailabilityChanged;

        public static string BackgroundTriggerName => MVARegistrationHelpers.BackgroundTriggerName;
        public static BackgroundTaskDeferral taskDeferral;
        public static BackgroundTaskDeferral TaskDeferral { 
            get 
            {
                return taskDeferral;
            }
            set 
            {
                BackgroundTaskComplete();
                taskDeferral = value;
            }
        }

        public static void Initialize(string token, string packageId)
        {
            //Unlock limited feature
            MVARegistrationHelpers.UnlockLimitedAccessFeature(token, packageId);

            //Background task registration
            MVARegistrationHelpers.IsBackgroundTaskRegistered = true;

            // Kick off the registration and/or retrieval of the 1st-stage keyword information
            Task.Run(async() => await SetupMvaKeywordAsync()).Wait();

            //transfer the detected result
            var detector = SignalDetectionHelper.Instance;
            detector.KeywordConfirmed += () => 
            {
                KeywordConfirmed?.Invoke();
            };
            detector.KeywordRejected += async() =>
            {
                await StopDetectionAsync().ConfigureAwait(false);
                KeywordRejected?.Invoke();
                BackgroundTaskComplete();
            };

            DialogManager.SessionExceptionAccured += (msg) =>
            {
                IsSessionExceptionAccured = true;
                SessionExceptionAccured?.Invoke(msg);
            };

            DialogManager.SetupConversationFailed +=(msg) =>
            {
                SetupConversationFailed?.Invoke(msg);
            };
            AppSharedState.UserAuthenticationChanged += AppSharedState_UserAuthenticationChanged;
        }
        public static event Action UserAuthenticationChanged;
        private static void AppSharedState_UserAuthenticationChanged()
        {
            UserAuthenticationChanged?.Invoke();
        }

        public static void SetMsSpeechConfig(MsSpeechRecognizerConfig config)
        {
            if(config != null && !string.IsNullOrEmpty(config.SubscriptionKey) && !string.IsNullOrEmpty(config.AzureRegion))
            {
                Debug.WriteLine($"SetMsSpeechConfig, SubscriptionKey:{config.SubscriptionKey}, AzureRegion:{config.AzureRegion}");
                LocalSettingsHelper.SpeechSubscriptionKey = config.SubscriptionKey;
                LocalSettingsHelper.AzureRegion = config.AzureRegion;
                LocalSettingsHelper.CustomSpeechId = config.CustomSpeechId;
            }
            else
            {
                Debug.WriteLine($"SetMsSpeechConfig, fail: Cannot get SubscriptionKey in debug file.");
            }
        }

        public static async Task<bool> GetIsUserAuthenticated()
        {
            var session = await AppSharedState.GetSessionAsync();
            if (session == null) return false;
            return session.IsUserAuthenticated;
        }

        public static async Task ForegroundActivationAsync()
        {
            Debug.WriteLine("ForegroundActivationAsync ... start.");
            var session = await AppSharedState.GetSessionAsync();
            ConversationalAgentState currentState = session.AgentState;
            await Recognizer.RequestAgentStateChangeAsync(ConversationalAgentState.Listening).ConfigureAwait(false);
            var ok = await session.RequestForegroundActivationAsync();

            Debug.WriteLine($"session.RequestForegroundActivationAsync , result:{ok}.");
            //to avoid entering compact mode
            //var infos = await AppDiagnosticInfo.RequestInfoForAppAsync();
            //Debug.WriteLine($"ForegroundActivationAsync..., {infos.Count}");
            //await infos[0].LaunchAsync();
            //var dispRequest = new DisplayRequest();
            //dispRequest.RequestActive();
            await Recognizer.RequestAgentStateChangeAsync(currentState).ConfigureAwait(false);
            Debug.WriteLine("ForegroundActivationAsync ... end.");
        }

        public static void ChangeBackend(IDialogBackend backend)
        {
            SignalDetectionHelper.Instance.ChangeDialogBackend(backend);
        }

        public static IDialogBackend Backend()
        {
            return SignalDetectionHelper.Instance.DialogBackend;
        }
        public static void StartDetection()
        {
            SignalDetectionHelper.Instance.HandleSignalDetection();
        }

        public static async Task StopDetectionAsync()
        {
            var dialogManager = SignalDetectionHelper.Instance.GetDialogManager();
            if (dialogManager != null)
            {
                await dialogManager.FinishConversationAsync().ConfigureAwait(false);
            }         
        }

        public static async Task<ConversationalAgentSession> GetSessionAsync()
        {
            return await AppSharedState.GetSessionAsync().ConfigureAwait(false);
        }

        public static async Task ResetAgentSessionAsync()
        {
            await AppSharedState.ResetAgentSessionAsync().ConfigureAwait(false);
            await AppSharedState.GetSessionAsync().ConfigureAwait(false);
        }

        public static async Task RequestAgentStateChangeAsync(ConversationalAgentState state)
        {
            Debug.WriteLine($"Recognizer.RequestAgentStateChangeAsync, Enter: {state}");
            var session = await AppSharedState.GetSessionAsync().ConfigureAwait(false);
            if (session != null && session.AgentState != state)
            {
                Debug.WriteLine($"session.RequestAgentStateChangeAsync: {state}");
                var res = await session.RequestAgentStateChangeAsync(state);
                Debug.WriteLine($"session.RequestAgentStateChangeAsync: {state}, res:{res}");
            }
            else
            {
                if (session == null)
                {
                    Debug.WriteLine($"RequestAgentStateChangeAsync: session == null.");
                }
                else
                {
                    Debug.WriteLine($"RequestAgentStateChangeAsync: session.AgentState:{session.AgentState}, state:{state}.");
                }
            }
            Debug.WriteLine($"Recognizer.RequestAgentStateChangeAsync, Exit: {state}");
        }

        private static async Task SetupMvaKeywordAsync()
        {
            Debug.WriteLine("DoKeywordSetupAsync");
            KeywordConfiguration = await KeywordRegistration.CreateActivationKeywordConfigurationAsync(KeywordRegistration.Keyword);
            if(KeywordConfiguration != null)
            {
                KeywordConfiguration.AvailabilityChanged += (s, e) =>
                {
                    Debug.WriteLine($"KeywordConfiguration.AvailabilityChanged," +
                        $" HasLockScreenPermission:{s.AvailabilityInfo.HasLockScreenPermission}, " +
                        $" HasPermission:{s.AvailabilityInfo.HasPermission}" +
                        $" IsEnabled:{s.AvailabilityInfo.IsEnabled}" +
                        $" HasSystemResourceAccess:{s.AvailabilityInfo.HasSystemResourceAccess}");
                    AvailabilityChanged?.Invoke(s, e);
                };
            }
            
            KeywordSetupFinished?.Invoke();
            Debug.WriteLine("DoKeywordSetupAsync...done");
        }

        public static void BackgroundTaskComplete()
        {
            if(taskDeferral != null)
            {
                Debug.WriteLine("BackgroundTaskComplete");
                taskDeferral.Complete();
                taskDeferral = null;
            }
        }

        public static ActivationSignalDetectionConfiguration KeywordConfiguration { get; private set; }

        public static bool IsActive
        {
            get
            {
                if (KeywordConfiguration != null)
                {
                    return KeywordConfiguration.IsActive;
                }
                else
                {
                    return false;
                }
            }
        }
        public static bool HasPermission { 
            get {  
                if (KeywordConfiguration != null)
                {
                    Debug.WriteLine($"KeywordConfiguration.AvailabilityInfo.HasPermission:{KeywordConfiguration.AvailabilityInfo.HasPermission}");
                    return KeywordConfiguration.AvailabilityInfo.HasPermission;
                }
                else
                {
                    Debug.WriteLine($"KeywordConfiguration == null");
                    return false;
                }
            } 
        }

        public static bool HasLockScreenPermission
        {
            get
            {
                if (KeywordConfiguration != null)
                {
                    return KeywordConfiguration.AvailabilityInfo.HasLockScreenPermission;
                }
                else
                {
                    return false;
                }
            }
        }

        public static async Task EnableFirstStageKws(bool enabled)
        {
            if(KeywordConfiguration != null)
            {
                await KeywordConfiguration.SetEnabledAsync(enabled);
            }
        }
    }
}
